Packages
library(tidyverse)
library(nlme)
library(plotly)
Importing Dataset
setwd("~/Insync/leonardogloria@uenf.br/Google Drive/MENTORIA_R/Monday_meeting")
dadosg <- read.table("data_growth.txt", h = T,na.strings=".") %>%
mutate(age=idade,w=peso,animf=factor(animal),class=factor(sexo)) %>%
groupedData(w~age|animf,data=.) #Creating dataset indicating animal repeated measures by age
Logistic model, without fixed effect
teta=c(); AIcc=c();
model00=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=dadosg,
start=list(fixed =c(30.4443882,12.7867281,0.06)),na.action=na.omit)
(teta[1]=attributes(logLik(model00))$df)
(AIcc[1]=-2*model00$logLik+2*teta[1]+(2*teta[1]*(teta[1]+1))/(nrow(dadosg)-teta[1]-1))
Logistic model with class of fixed effect, in other words, we will fit a parameter for each sex
st01=c(rep(summary(model00)$ coefficients$fixed[[1]],length(levels(dadosg$class))),rep(summary(model00)$ coefficients$fixed[[2]],length(levels(dadosg$class))),rep(summary(model00)$ coefficients$fixed[[3]],length(levels(dadosg$class)))) #starting values based on the model00 output
model01=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=500),
data=dadosg,start=st01,na.action=na.omit)
(teta[2]=attributes(logLik(model01))$df)
(AIcc[2]=-2*model01$logLik+2*teta[2]+(2*teta[2]*(teta[2]+1))/(nrow(dadosg)-teta[2]-1))
Logistic model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (weights = heterogeneous variance along the time, varPower= power variance function is defined as s2(v) = |v|^(2*t))
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model02=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=500),
data=dadosg,start=parf,na.action=na.omit,weights= varPower())
intervals(model02)
######################
(teta[3]=attributes(logLik(model02))$df)
(AIcc[3]=-2*model02$logLik+2*teta[3]+(2*teta[3]*(teta[3]+1))/(nrow(dadosg)-teta[3]-1))
Logistic model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (corr = modeling animal repeated measures, CAR1 = continous autoregressive)
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model03=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1())
######################
(teta[4]=attributes(logLik(model03))$df)
(AIcc[4]=-2*model03$logLik+2*teta[4]+(2*teta[4]*(teta[4]+1))/(nrow(dadosg)-teta[4]-1))
Full Logistic model with class of fixed effect, heterogeneous variance along the time, repeated measures
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(w~a/(1+b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a~1),control=nlmeControl(minScale=10**-100,maxIter=500),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower()) #logistico
######################
(teta[5]=attributes(logLik(model04))$df)
(AIcc[5]=-2*model04$logLik+2*teta[5]+(2*teta[5]*(teta[5]+1))/(nrow(dadosg)-teta[5]-1))
Gompertz model, without fixed effect
teta=c(); AIcc=c();
model05=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=dadosg,
start=list(fixed =c(30.4443882,12.7867281,0.06)),na.action=na.omit)
(teta[6]=attributes(logLik(model05))$df)
(AIcc[6]=-2*model05$logLik+2*teta[6]+(2*teta[6]*(teta[6]+1))/(nrow(dadosg)-teta[6]-1))
Gompertz model with class of fixed effect, in other words, we will fit a parameter for each sex
st05=c(rep(summary(model05)$ coefficients$fixed[[1]],length(levels(dadosg$class))),rep(summary(model05)$ coefficients$fixed[[2]],length(levels(dadosg$class))),rep(summary(model05)$ coefficients$fixed[[3]],length(levels(dadosg$class)))) #starting values based on the model05 output
model06=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=st01,na.action=na.omit)
(teta[7]=attributes(logLik(model06))$df)
(AIcc[7]=-2*model06$logLik+2*teta[7]+(2*teta[7]*(teta[7]+1))/(nrow(dadosg)-teta[7]-1))
Gompertz model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (weights = heterogeneous variance along the time, varPower= power variance function is defined as s2(v) = |v|^(2*t))
parf=c()
for (i in 1:length(summary(model06)$ coefficients$fixed)) {
par1=summary(model06)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model07=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=parf,na.action=na.omit,weights= varPower())
intervals(model07)
######################
(teta[8]=attributes(logLik(model07))$df)
(AIcc[8]=-2*model07$logLik+2*teta[8]+(2*teta[8]*(teta[8]+1))/(nrow(dadosg)-teta[8]-1))
Gompertz model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (corr = modeling animal repeated measures, CAR1 = continous autoregressive)
parf=c()
for (i in 1:length(summary(model06)$ coefficients$fixed)) {
par1=summary(model06)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model08=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+b+k~1),control=nlmeControl(maxIter=100),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1())
######################
(teta[9]=attributes(logLik(model08))$df)
(AIcc[9]=-2*model08$logLik+2*teta[9]+(2*teta[9]*(teta[9]+1))/(nrow(dadosg)-teta[9]-1))
Full Gompertz model with class of fixed effect, heterogeneous variance along the time, repeated measures
parf=c()
for (i in 1:length(summary(model06)$ coefficients$fixed)) {
par1=summary(model06)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model09=nlme(w~a*exp(-b*(exp(-k*age))),fixed=list(a+b+k~class-1),
random=pdDiag(a+k~1),control=nlmeControl(minScale=10**-100,maxIter=500),
data=dadosg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
######################
(teta[10]=attributes(logLik(model09))$df)
(AIcc[10]=-2*model09$logLik+2*teta[10]+(2*teta[10]*(teta[10]+1))/(nrow(dadosg)-teta[10]-1))
multi-model selection framework (Burnham and Anderson, 2004)
delta=c()
for(i in 1: length(AIcc)){
delta[i]=AIcc[i]-min(AIcc)
}
wpro=c()
for(i in 1:length(AIcc)){
wpro[i]=exp(-delta[i]/2)
}
sum(wpro[1:length(AIcc)])
wprob=c()
for(i in 1: length(AIcc)){
wprob[i]=exp(-delta[i]/2)/sum(wpro[1:length(AIcc)])
}
ER=c()
for(i in 1: length(AIcc)){
ER[i]=max(wprob)/wprob[i]
}
(quadro.akaike=data.frame(teta,AICc=AIcc,delta,wprob,ER))
Prediction and Growth rate function
Gompertzhat=function(a,b,k,age){
y=a*exp(-b*(exp(-k*age)))
return(y)
}
GRGompertz=function(a,b,k,age){
y=a * (exp(-b * (exp(-k * age))) * (b * (exp(-k * age) * k)))
return(y)
}
creating interactive graphics with plotly
setwd("~/Insync/leonardogloria@uenf.br/Google Drive/MENTORIA_R/Monday_meeting")
param=read.table("parameters.txt",h=T)
row.names(param)=c("Males","Females")
time=seq(1,90,by=0.25)
#predictions
predc1=Gompertzhat(a=param[1,1],b=param[1,2],k=param[1,3],age =time)
predc2=Gompertzhat(a=param[2,1],b=param[2,2],k=param[2,3],age =time)
#growth rate
grc1=GRGompertz(a=param[1,1],b=param[1,2],k=param[1,3],age =time)
grc2=GRGompertz(a=param[2,1],b=param[2,2],k=param[2,3],age =time)
#inflaction point (max efficiency)
maxef1=data.frame(grc1)
rownames(maxef1)=time
maxefx1=as.numeric(rownames(maxef1)[which.max(maxef1$grc1)])
maxefy1=maxef1$grc1[which.max(maxef1$grc1)]
maxef2=data.frame(grc2)
rownames(maxef2)=time
maxefx2=as.numeric(rownames(maxef2)[which.max(maxef2$grc2)])
maxefy2=maxef2$grc2[which.max(maxef2$grc2)]
### Multiple Y Axes
ay <- list(
tickfont = list(color = "black"),
overlaying = "y",
side = "right",
title = "Daily Weight Gain(g)"
)
fig <- plot_ly()
fig <- fig %>% add_lines(x = time, y = predc1, name = "Weight Males")
fig <- fig %>% add_lines(x = time, y = grc1, name = "Growth Rate Males", yaxis = "y2")
fig <- fig %>% add_markers(x = maxefx1, y = maxefy1, name = "Max Growth Rate Males", yaxis = "y2")
fig <- fig %>% add_segments(line=list(type="dash"),x = maxefx1,xend=maxefx1, y = 0,yend =maxefy1, yaxis = "y2",name = "Max Growth Rate Males")
fig <- fig %>% add_lines(x = time, y = predc2, name = "Weight Females")
fig <- fig %>% add_lines(x = time, y = grc2, name = "Growth Rate Females", yaxis = "y2")
fig <- fig %>% add_markers(x = maxefx2, y = maxefy2, name = "Max Growth Rate Females", yaxis = "y2")
fig <- fig %>% add_segments(color = I("gray"),x = maxefx2,xend=maxefx2, y = 0,yend =maxefy2, yaxis = "y2",name = "Max Growth Rate Females")
fig <- fig %>% layout(
title = "Growth Curve with Growth rate", yaxis2 = ay,
xaxis = list(title="Age(days)"),
yaxis = list(title="Weight(g)")
)
fig
Importing Milking Dataset
Importing dataset from txt
setwd("~/Insync/leonardogloria@uenf.br/Google Drive/MENTORIA_R/Monday_meeting")
data_milkg <- read.table("data_milk.txt", h = T) %>%
mutate(animal=factor(animal),CG=factor(CG)) %>%
groupedData(MY~DIM|animal,data=.) #Creating dataset indicating animal repeated measures by days in milk
WOOD model, without fixed effect
teta=c(); AIcc=c();
model00=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~1),random=pdDiag(a+b~1),
control=nlmeControl(maxIter=100),
data=data_milkg,
start=list(fixed =c(20,0.1,0.003)),na.action=na.omit)
(teta[1]=attributes(logLik(model00))$df)
[1] 6
(AIcc[1]=-2*model00$logLik+2*teta[1]+(2*teta[1]*(teta[1]+1))/(nrow(data_milkg)-teta[1]-1))
[1] 13428.63
WOOD model with class of fixed effect, in other words, we will fit a parameter for each sex
st01=c(rep(summary(model00)$ coefficients$fixed[[1]],length(levels(data_milkg$CG))),rep(summary(model00)$ coefficients$fixed[[2]],length(levels(data_milkg$CG))),rep(summary(model00)$ coefficients$fixed[[3]],length(levels(data_milkg$CG)))) #starting values based on the model00 output
model01=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=st01,na.action=na.omit)
(teta[2]=attributes(logLik(model01))$df)
[1] 20
(AIcc[2]=-2*model01$logLik+2*teta[2]+(2*teta[2]*(teta[2]+1))/(nrow(data_milkg)-teta[2]-1))
[1] 13355.63
WOOD model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (weights = heterogeneous variance along the time, varPower= power variance function is defined as s2(v) = |v|^(2*t))
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model02=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a~1),
control=nlmeControl(maxIter=500),
data=data_milkg,start=parf,na.action=na.omit,weights= varPower())
intervals(model02)
######################
(teta[3]=attributes(logLik(model02))$df)
(AIcc[3]=-2*model02$logLik+2*teta[3]+(2*teta[3]*(teta[3]+1))/(nrow(data_milkg)-teta[3]-1))
WOOD model with class of fixed effect, in other words, we will fit a parameter for each sex, plus (corr = modeling animal repeated measures, CAR1 = continous autoregressive)
parf=c()
for (i in 1:length(summary(model01)$ coefficients$fixed)) {
par1=summary(model01)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
} #starting values based on the model01 output, each class has its own start value
model03=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1())
######################
(teta[4]=attributes(logLik(model03))$df)
[1] 21
(AIcc[4]=-2*model03$logLik+2*teta[4]+(2*teta[4]*(teta[4]+1))/(nrow(data_milkg)-teta[4]-1))
[1] 12224.5
Full WOOD model with class of fixed effect, heterogeneous variance along the time, repeated measures
parf=c()
for (i in 1:length(summary(model03)$ coefficients$fixed)) {
par1=summary(model03)$ coefficients$fixed[[i]]
parf=rbind(parf,par1)
}
model04=nlme(MY~a*(DIM**b)*exp(-k*DIM),fixed=list(a+b+k~CG-1),random=pdDiag(a+b+k~1),
control=nlmeControl(maxIter=100),
data=data_milkg,start=parf,na.action=na.omit,corr=corCAR1(),weights= varPower())
multi-model selection framework (Burnham and Anderson, 2004)
delta=c()
for(i in 1: length(AIcc)){
delta[i]=AIcc[i]-min(AIcc)
}
wpro=c()
for(i in 1:length(AIcc)){
wpro[i]=exp(-delta[i]/2)
}
sum(wpro[1:length(AIcc)])
[1] 1
wprob=c()
for(i in 1: length(AIcc)){
wprob[i]=exp(-delta[i]/2)/sum(wpro[1:length(AIcc)])
}
ER=c()
for(i in 1: length(AIcc)){
ER[i]=max(wprob)/wprob[i]
}
(quadro.akaike=data.frame(teta,AICc=AIcc,delta,wprob,ER))
Prediction MY and TMY
MWoodhat=function(a,b,k,age){
y=(a*age^b)*exp(-k*age)
return(y)
}
Total Milk Yield
(TMYCG22=MYWood(a=as.numeric(intervals(model04)$fixed[2,2]),
b=as.numeric(intervals(model04)$fixed[8,2]),
k=as.numeric(intervals(model04)$fixed[14,2]),
MYl=10,MYu=305))
value
[1,] 6900.513
(TMYCG32=MYWood(a=as.numeric(intervals(model04)$fixed[5,2]),
b=as.numeric(intervals(model04)$fixed[11,2]),
k=as.numeric(intervals(model04)$fixed[17,2]),
MYl=10,MYu=305))
value
[1,] 6739.791
TMYCG22[[1]]-TMYCG32[[1]]
[1] 160.7221
creating interactive graphics with plotly
age=seq(1,305,by=0.25)
#predictions
predc1=MWoodhat(a=as.numeric(intervals(model04)$fixed[2,2]),b=as.numeric(intervals(model04)$fixed[8,2]),k=as.numeric(intervals(model04)$fixed[14,2]),age=age)
predc2=MWoodhat(a=as.numeric(intervals(model04)$fixed[5,2]),b=as.numeric(intervals(model04)$fixed[11,2]),k=as.numeric(intervals(model04)$fixed[17,2]),age=age)
### Multiple Y Axes
#ay <- list(
# tickfont = list(color = "black"),
# overlaying = "y",
# side = "right",
# title = "Daily Weight Gain(g)"
#)
fig <- plot_ly()
fig <- fig %>% add_lines(x = age, y = predc1, name = "Milk Yield CG22")
fig <- fig %>% add_lines(x = age, y = predc2, name = "Milk Yield CG32")
fig <- fig %>% layout(
title = "WOOD Milk curve",
xaxis = list(title="DIM(days)"),
yaxis = list(title="MY (kg)")
)
fig
LS0tCnRpdGxlOiAiTm9uLUxpbmVhciBNaXhlZCBNb2RlbHMiCmF1dGhvcjogIkJyaXRvJ3MgTGFiIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKaGlnaGxpZ2h0OiB0YW5nbwotLS0KCjxocj4KCmBgYHtyIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGluY2x1ZGUgPSBULCBlY2hvID1ULCBtZXNzYWdlPUYsIHdhcm5pbmc9RikKYGBgCgojIyMjIFBhY2thZ2VzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobmxtZSkKbGlicmFyeShwbG90bHkpCmBgYAoKIyMjIyMgSW1wb3J0aW5nIERhdGFzZXQKCmBgYHtyfQpzZXR3ZCgifi9JbnN5bmMvbGVvbmFyZG9nbG9yaWFAdWVuZi5ici9Hb29nbGUgRHJpdmUvTUVOVE9SSUFfUi9Nb25kYXlfbWVldGluZyIpCmRhZG9zZyA8LSByZWFkLnRhYmxlKCJkYXRhX2dyb3d0aC50eHQiLCBoID0gVCxuYS5zdHJpbmdzPSIuIikgJT4lIAogIG11dGF0ZShhZ2U9aWRhZGUsdz1wZXNvLGFuaW1mPWZhY3RvcihhbmltYWwpLGNsYXNzPWZhY3RvcihzZXhvKSkgJT4lIAogIGdyb3VwZWREYXRhKHd+YWdlfGFuaW1mLGRhdGE9LikgI0NyZWF0aW5nIGRhdGFzZXQgaW5kaWNhdGluZyBhbmltYWwgcmVwZWF0ZWQgbWVhc3VyZXMgYnkgYWdlCgpgYGAKCiMjIyMjIExvZ2lzdGljIG1vZGVsLCB3aXRob3V0IGZpeGVkIGVmZmVjdApgYGB7cn0KdGV0YT1jKCk7ICAgQUljYz1jKCk7Cgptb2RlbDAwPW5sbWUod35hLygxK2IqKGV4cCgtayphZ2UpKSksZml4ZWQ9bGlzdChhK2Ira34xKSxyYW5kb209cGREaWFnKGF+MSksCiAgICAgICAgICAgICBjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLAogICAgICAgICAgICAgc3RhcnQ9bGlzdChmaXhlZCA9YygzMC40NDQzODgyLDEyLjc4NjcyODEsMC4wNikpLG5hLmFjdGlvbj1uYS5vbWl0KSAgICAgICAKCih0ZXRhWzFdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDApKSRkZikKKEFJY2NbMV09LTIqbW9kZWwwMCRsb2dMaWsrMip0ZXRhWzFdKygyKnRldGFbMV0qKHRldGFbMV0rMSkpLyhucm93KGRhZG9zZyktdGV0YVsxXS0xKSkKYGBgCgojIyMjIyAgTG9naXN0aWMgbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGluIG90aGVyIHdvcmRzLCB3ZSB3aWxsIGZpdCBhIHBhcmFtZXRlciBmb3IgZWFjaCBzZXgKYGBge3J9CnN0MDE9YyhyZXAoc3VtbWFyeShtb2RlbDAwKSQgY29lZmZpY2llbnRzJGZpeGVkW1sxXV0sbGVuZ3RoKGxldmVscyhkYWRvc2ckY2xhc3MpKSkscmVwKHN1bW1hcnkobW9kZWwwMCkkIGNvZWZmaWNpZW50cyRmaXhlZFtbMl1dLGxlbmd0aChsZXZlbHMoZGFkb3NnJGNsYXNzKSkpLHJlcChzdW1tYXJ5KG1vZGVsMDApJCBjb2VmZmljaWVudHMkZml4ZWRbWzNdXSxsZW5ndGgobGV2ZWxzKGRhZG9zZyRjbGFzcykpKSkgI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMCBvdXRwdXQKCm1vZGVsMDE9bmxtZSh3fmEvKDErYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9NTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXN0MDEsbmEuYWN0aW9uPW5hLm9taXQpCgoKKHRldGFbMl09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwMSkpJGRmKQooQUljY1syXT0tMiptb2RlbDAxJGxvZ0xpaysyKnRldGFbMl0rKDIqdGV0YVsyXSoodGV0YVsyXSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzJdLTEpKQpgYGAKCiMjIyMjICBMb2dpc3RpYyBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleCwgcGx1cyAod2VpZ2h0cyA9IGhldGVyb2dlbmVvdXMgdmFyaWFuY2UgYWxvbmcgdGhlIHRpbWUsIHZhclBvd2VyPSBwb3dlciB2YXJpYW5jZSBmdW5jdGlvbiBpcyBkZWZpbmVkIGFzIHMyKHYpID0gfHZ8XigyKnQpKQoKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDI9bmxtZSh3fmEvKDErYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9NTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsd2VpZ2h0cz0gdmFyUG93ZXIoKSkgICAgICAgCgppbnRlcnZhbHMobW9kZWwwMikKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbM109YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwMikpJGRmKQooQUljY1szXT0tMiptb2RlbDAyJGxvZ0xpaysyKnRldGFbM10rKDIqdGV0YVszXSoodGV0YVszXSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzNdLTEpKQpgYGAKCiMjIyMjICBMb2dpc3RpYyBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleCwgcGx1cyAoY29yciA9IG1vZGVsaW5nIGFuaW1hbCByZXBlYXRlZCBtZWFzdXJlcywgQ0FSMSA9IGNvbnRpbm91cyBhdXRvcmVncmVzc2l2ZSkKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDEpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDM9bmxtZSh3fmEvKDErYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsY29ycj1jb3JDQVIxKCkpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCih0ZXRhWzRdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDMpKSRkZikKKEFJY2NbNF09LTIqbW9kZWwwMyRsb2dMaWsrMip0ZXRhWzRdKygyKnRldGFbNF0qKHRldGFbNF0rMSkpLyhucm93KGRhZG9zZyktdGV0YVs0XS0xKSkKYGBgCgojIyMjIyBGdWxsIExvZ2lzdGljIG1vZGVsIHdpdGggY2xhc3Mgb2YgZml4ZWQgZWZmZWN0LCBoZXRlcm9nZW5lb3VzIHZhcmlhbmNlIGFsb25nIHRoZSB0aW1lLCByZXBlYXRlZCBtZWFzdXJlcwpgYGB7cn0KcGFyZj1jKCkKZm9yIChpIGluIDE6bGVuZ3RoKHN1bW1hcnkobW9kZWwwMykkIGNvZWZmaWNpZW50cyRmaXhlZCkpICAgICAgewpwYXIxPXN1bW1hcnkobW9kZWwwMykkIGNvZWZmaWNpZW50cyRmaXhlZFtbaV1dCnBhcmY9cmJpbmQocGFyZixwYXIxKQogfQoKbW9kZWwwND1ubG1lKHd+YS8oMStiKihleHAoLWsqYWdlKSkpLGZpeGVkPWxpc3QoYStiK2t+Y2xhc3MtMSksCiAgICAgICAgICAgICByYW5kb209cGREaWFnKGF+MSksY29udHJvbD1ubG1lQ29udHJvbChtaW5TY2FsZT0xMCoqLTEwMCxtYXhJdGVyPTUwMCksCiAgICAgICAgICAgICBkYXRhPWRhZG9zZyxzdGFydD1wYXJmLG5hLmFjdGlvbj1uYS5vbWl0LGNvcnI9Y29yQ0FSMSgpLHdlaWdodHM9IHZhclBvd2VyKCkpICAgICAgICNsb2dpc3RpY28KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbNV09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwNCkpJGRmKQooQUljY1s1XT0tMiptb2RlbDA0JGxvZ0xpaysyKnRldGFbNV0rKDIqdGV0YVs1XSoodGV0YVs1XSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzVdLTEpKQpgYGAKCiMjIyMjIEdvbXBlcnR6IG1vZGVsLCB3aXRob3V0IGZpeGVkIGVmZmVjdApgYGB7cn0KdGV0YT1jKCk7ICAgQUljYz1jKCk7Cgptb2RlbDA1PW5sbWUod35hKmV4cCgtYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfjEpLHJhbmRvbT1wZERpYWcoYX4xKSwKICAgICAgICAgICAgIGNvbnRyb2w9bmxtZUNvbnRyb2wobWF4SXRlcj0xMDApLAogICAgICAgICAgICAgZGF0YT1kYWRvc2csCiAgICAgICAgICAgICBzdGFydD1saXN0KGZpeGVkID1jKDMwLjQ0NDM4ODIsMTIuNzg2NzI4MSwwLjA2KSksbmEuYWN0aW9uPW5hLm9taXQpICAgICAgIAoKKHRldGFbNl09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwNSkpJGRmKQooQUljY1s2XT0tMiptb2RlbDA1JGxvZ0xpaysyKnRldGFbNl0rKDIqdGV0YVs2XSoodGV0YVs2XSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzZdLTEpKQpgYGAKCiMjIyMjICBHb21wZXJ0eiBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleApgYGB7cn0Kc3QwNT1jKHJlcChzdW1tYXJ5KG1vZGVsMDUpJCBjb2VmZmljaWVudHMkZml4ZWRbWzFdXSxsZW5ndGgobGV2ZWxzKGRhZG9zZyRjbGFzcykpKSxyZXAoc3VtbWFyeShtb2RlbDA1KSQgY29lZmZpY2llbnRzJGZpeGVkW1syXV0sbGVuZ3RoKGxldmVscyhkYWRvc2ckY2xhc3MpKSkscmVwKHN1bW1hcnkobW9kZWwwNSkkIGNvZWZmaWNpZW50cyRmaXhlZFtbM11dLGxlbmd0aChsZXZlbHMoZGFkb3NnJGNsYXNzKSkpKSAjc3RhcnRpbmcgdmFsdWVzIGJhc2VkIG9uIHRoZSBtb2RlbDA1IG91dHB1dAoKbW9kZWwwNj1ubG1lKHd+YSpleHAoLWIqKGV4cCgtayphZ2UpKSksZml4ZWQ9bGlzdChhK2Ira35jbGFzcy0xKSwKICAgICAgICAgICAgIHJhbmRvbT1wZERpYWcoYStiK2t+MSksY29udHJvbD1ubG1lQ29udHJvbChtYXhJdGVyPTEwMCksCiAgICAgICAgICAgICBkYXRhPWRhZG9zZyxzdGFydD1zdDAxLG5hLmFjdGlvbj1uYS5vbWl0KQoKCih0ZXRhWzddPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDYpKSRkZikKKEFJY2NbN109LTIqbW9kZWwwNiRsb2dMaWsrMip0ZXRhWzddKygyKnRldGFbN10qKHRldGFbN10rMSkpLyhucm93KGRhZG9zZyktdGV0YVs3XS0xKSkKYGBgCgojIyMjIyAgR29tcGVydHogbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGluIG90aGVyIHdvcmRzLCB3ZSB3aWxsIGZpdCBhIHBhcmFtZXRlciBmb3IgZWFjaCBzZXgsIHBsdXMgKHdlaWdodHMgPSBoZXRlcm9nZW5lb3VzIHZhcmlhbmNlIGFsb25nIHRoZSB0aW1lLCB2YXJQb3dlcj0gcG93ZXIgdmFyaWFuY2UgZnVuY3Rpb24gaXMgZGVmaW5lZCBhcyBzMih2KSA9IHx2fF4oMip0KSkKCmBgYHtyfQpwYXJmPWMoKQpmb3IgKGkgaW4gMTpsZW5ndGgoc3VtbWFyeShtb2RlbDA2KSQgY29lZmZpY2llbnRzJGZpeGVkKSkgICAgICB7CnBhcjE9c3VtbWFyeShtb2RlbDA2KSQgY29lZmZpY2llbnRzJGZpeGVkW1tpXV0KcGFyZj1yYmluZChwYXJmLHBhcjEpCiB9ICNzdGFydGluZyB2YWx1ZXMgYmFzZWQgb24gdGhlIG1vZGVsMDEgb3V0cHV0LCBlYWNoIGNsYXNzIGhhcyBpdHMgb3duIHN0YXJ0IHZhbHVlCgptb2RlbDA3PW5sbWUod35hKmV4cCgtYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2Ira34xKSxjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9MTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGFkb3NnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsd2VpZ2h0cz0gdmFyUG93ZXIoKSkgICAgICAgCgppbnRlcnZhbHMobW9kZWwwNykKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbOF09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwNykpJGRmKQooQUljY1s4XT0tMiptb2RlbDA3JGxvZ0xpaysyKnRldGFbOF0rKDIqdGV0YVs4XSoodGV0YVs4XSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzhdLTEpKQpgYGAKCiMjIyMjICBHb21wZXJ0eiBtb2RlbCB3aXRoIGNsYXNzIG9mIGZpeGVkIGVmZmVjdCwgaW4gb3RoZXIgd29yZHMsIHdlIHdpbGwgZml0IGEgcGFyYW1ldGVyIGZvciBlYWNoIHNleCwgcGx1cyAoY29yciA9IG1vZGVsaW5nIGFuaW1hbCByZXBlYXRlZCBtZWFzdXJlcywgQ0FSMSA9IGNvbnRpbm91cyBhdXRvcmVncmVzc2l2ZSkKYGBge3J9CnBhcmY9YygpCmZvciAoaSBpbiAxOmxlbmd0aChzdW1tYXJ5KG1vZGVsMDYpJCBjb2VmZmljaWVudHMkZml4ZWQpKSAgICAgIHsKcGFyMT1zdW1tYXJ5KG1vZGVsMDYpJCBjb2VmZmljaWVudHMkZml4ZWRbW2ldXQpwYXJmPXJiaW5kKHBhcmYscGFyMSkKIH0gI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMSBvdXRwdXQsIGVhY2ggY2xhc3MgaGFzIGl0cyBvd24gc3RhcnQgdmFsdWUKCm1vZGVsMDg9bmxtZSh3fmEqZXhwKC1iKihleHAoLWsqYWdlKSkpLGZpeGVkPWxpc3QoYStiK2t+Y2xhc3MtMSksCiAgICAgICAgICAgICByYW5kb209cGREaWFnKGErYitrfjEpLGNvbnRyb2w9bmxtZUNvbnRyb2wobWF4SXRlcj0xMDApLAogICAgICAgICAgICAgZGF0YT1kYWRvc2csc3RhcnQ9cGFyZixuYS5hY3Rpb249bmEub21pdCxjb3JyPWNvckNBUjEoKSkKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbOV09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwOCkpJGRmKQooQUljY1s5XT0tMiptb2RlbDA4JGxvZ0xpaysyKnRldGFbOV0rKDIqdGV0YVs5XSoodGV0YVs5XSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzldLTEpKQpgYGAKCiMjIyMjIEZ1bGwgR29tcGVydHogbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGhldGVyb2dlbmVvdXMgdmFyaWFuY2UgYWxvbmcgdGhlIHRpbWUsIHJlcGVhdGVkIG1lYXN1cmVzCmBgYHtyfQpwYXJmPWMoKQpmb3IgKGkgaW4gMTpsZW5ndGgoc3VtbWFyeShtb2RlbDA2KSQgY29lZmZpY2llbnRzJGZpeGVkKSkgICAgICB7CnBhcjE9c3VtbWFyeShtb2RlbDA2KSQgY29lZmZpY2llbnRzJGZpeGVkW1tpXV0KcGFyZj1yYmluZChwYXJmLHBhcjEpCiB9ICNzdGFydGluZyB2YWx1ZXMgYmFzZWQgb24gdGhlIG1vZGVsMDEgb3V0cHV0LCBlYWNoIGNsYXNzIGhhcyBpdHMgb3duIHN0YXJ0IHZhbHVlCgptb2RlbDA5PW5sbWUod35hKmV4cCgtYiooZXhwKC1rKmFnZSkpKSxmaXhlZD1saXN0KGErYitrfmNsYXNzLTEpLAogICAgICAgICAgICAgcmFuZG9tPXBkRGlhZyhhK2t+MSksY29udHJvbD1ubG1lQ29udHJvbChtaW5TY2FsZT0xMCoqLTEwMCxtYXhJdGVyPTUwMCksCiAgICAgICAgICAgICBkYXRhPWRhZG9zZyxzdGFydD1wYXJmLG5hLmFjdGlvbj1uYS5vbWl0LGNvcnI9Y29yQ0FSMSgpLHdlaWdodHM9IHZhclBvd2VyKCkpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCih0ZXRhWzEwXT1hdHRyaWJ1dGVzKGxvZ0xpayhtb2RlbDA5KSkkZGYpCihBSWNjWzEwXT0tMiptb2RlbDA5JGxvZ0xpaysyKnRldGFbMTBdKygyKnRldGFbMTBdKih0ZXRhWzEwXSsxKSkvKG5yb3coZGFkb3NnKS10ZXRhWzEwXS0xKSkKYGBgCgojIyMjIyBtdWx0aS1tb2RlbCBzZWxlY3Rpb24gZnJhbWV3b3JrIChCdXJuaGFtIGFuZCBBbmRlcnNvbiwgMjAwNCkKYGBge3J9CgpkZWx0YT1jKCkKZm9yKGkgaW4gMTogbGVuZ3RoKEFJY2MpKXsKZGVsdGFbaV09QUljY1tpXS1taW4oQUljYykKfQoKd3Bybz1jKCkKZm9yKGkgaW4gMTpsZW5ndGgoQUljYykpewp3cHJvW2ldPWV4cCgtZGVsdGFbaV0vMikKfQpzdW0od3Byb1sxOmxlbmd0aChBSWNjKV0pCgp3cHJvYj1jKCkKZm9yKGkgaW4gMTogbGVuZ3RoKEFJY2MpKXsKd3Byb2JbaV09ZXhwKC1kZWx0YVtpXS8yKS9zdW0od3Byb1sxOmxlbmd0aChBSWNjKV0pCn0KCgpFUj1jKCkKZm9yKGkgaW4gMTogbGVuZ3RoKEFJY2MpKXsKRVJbaV09bWF4KHdwcm9iKS93cHJvYltpXQp9CgoocXVhZHJvLmFrYWlrZT1kYXRhLmZyYW1lKHRldGEsQUlDYz1BSWNjLGRlbHRhLHdwcm9iLEVSKSkKYGBgCgoKIyMjIyMgRXh0cmFjdGluZyB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgb2YgdGhlIHBhcmFtZXRlcnMgYWZ0ZXIgY2hvb3NpbmcgdGhlIGJlc3QgbW9kZWwKYGBge3J9CmludGVydmFscyhtb2RlbDA5KQpgYGAKCiMjIyMjIFByZWRpY3Rpb24gYW5kIEdyb3d0aCByYXRlIGZ1bmN0aW9uCmBgYHtyfQpHb21wZXJ0emhhdD1mdW5jdGlvbihhLGIsayxhZ2UpewogIHk9YSpleHAoLWIqKGV4cCgtayphZ2UpKSkKICByZXR1cm4oeSkKfQpHUkdvbXBlcnR6PWZ1bmN0aW9uKGEsYixrLGFnZSl7CiAgeT1hICogKGV4cCgtYiAqIChleHAoLWsgKiBhZ2UpKSkgKiAoYiAqIChleHAoLWsgKiBhZ2UpICogaykpKQogIHJldHVybih5KQp9CmBgYAoKCiMjIyMjIGNyZWF0aW5nIGludGVyYWN0aXZlIGdyYXBoaWNzIHdpdGggcGxvdGx5CmBgYHtyfQpzZXR3ZCgifi9JbnN5bmMvbGVvbmFyZG9nbG9yaWFAdWVuZi5ici9Hb29nbGUgRHJpdmUvTUVOVE9SSUFfUi9Nb25kYXlfbWVldGluZyIpCnBhcmFtPXJlYWQudGFibGUoInBhcmFtZXRlcnMudHh0IixoPVQpCnJvdy5uYW1lcyhwYXJhbSk9YygiTWFsZXMiLCJGZW1hbGVzIikKCnRpbWU9c2VxKDEsOTAsYnk9MC4yNSkKI3ByZWRpY3Rpb25zCnByZWRjMT1Hb21wZXJ0emhhdChhPXBhcmFtWzEsMV0sYj1wYXJhbVsxLDJdLGs9cGFyYW1bMSwzXSxhZ2UgPXRpbWUpCnByZWRjMj1Hb21wZXJ0emhhdChhPXBhcmFtWzIsMV0sYj1wYXJhbVsyLDJdLGs9cGFyYW1bMiwzXSxhZ2UgPXRpbWUpCgojZ3Jvd3RoIHJhdGUKZ3JjMT1HUkdvbXBlcnR6KGE9cGFyYW1bMSwxXSxiPXBhcmFtWzEsMl0saz1wYXJhbVsxLDNdLGFnZSA9dGltZSkKZ3JjMj1HUkdvbXBlcnR6KGE9cGFyYW1bMiwxXSxiPXBhcmFtWzIsMl0saz1wYXJhbVsyLDNdLGFnZSA9dGltZSkKCiNpbmZsYWN0aW9uIHBvaW50IChtYXggZWZmaWNpZW5jeSkKbWF4ZWYxPWRhdGEuZnJhbWUoZ3JjMSkKcm93bmFtZXMobWF4ZWYxKT10aW1lCgptYXhlZngxPWFzLm51bWVyaWMocm93bmFtZXMobWF4ZWYxKVt3aGljaC5tYXgobWF4ZWYxJGdyYzEpXSkKbWF4ZWZ5MT1tYXhlZjEkZ3JjMVt3aGljaC5tYXgobWF4ZWYxJGdyYzEpXQoKbWF4ZWYyPWRhdGEuZnJhbWUoZ3JjMikKcm93bmFtZXMobWF4ZWYyKT10aW1lCgptYXhlZngyPWFzLm51bWVyaWMocm93bmFtZXMobWF4ZWYyKVt3aGljaC5tYXgobWF4ZWYyJGdyYzIpXSkKbWF4ZWZ5Mj1tYXhlZjIkZ3JjMlt3aGljaC5tYXgobWF4ZWYyJGdyYzIpXQoKIyMjIE11bHRpcGxlIFkgQXhlcwoKYXkgPC0gbGlzdCgKICB0aWNrZm9udCA9IGxpc3QoY29sb3IgPSAiYmxhY2siKSwKICBvdmVybGF5aW5nID0gInkiLAogIHNpZGUgPSAicmlnaHQiLAogIHRpdGxlID0gIkRhaWx5IFdlaWdodCBHYWluKGcpIgopCmZpZyA8LSBwbG90X2x5KCkKZmlnIDwtIGZpZyAlPiUgYWRkX2xpbmVzKHggPSB0aW1lLCB5ID0gcHJlZGMxLCBuYW1lID0gIldlaWdodCBNYWxlcyIpCmZpZyA8LSBmaWcgJT4lIGFkZF9saW5lcyh4ID0gdGltZSwgeSA9IGdyYzEsIG5hbWUgPSAiR3Jvd3RoIFJhdGUgTWFsZXMiLCB5YXhpcyA9ICJ5MiIpCmZpZyA8LSBmaWcgJT4lIGFkZF9tYXJrZXJzKHggPSBtYXhlZngxLCB5ID0gbWF4ZWZ5MSwgbmFtZSA9ICJNYXggR3Jvd3RoIFJhdGUgTWFsZXMiLCB5YXhpcyA9ICJ5MiIpCmZpZyA8LSBmaWcgJT4lIGFkZF9zZWdtZW50cyhsaW5lPWxpc3QodHlwZT0iZGFzaCIpLHggPSBtYXhlZngxLHhlbmQ9bWF4ZWZ4MSwgeSA9IDAseWVuZCA9bWF4ZWZ5MSwgeWF4aXMgPSAieTIiLG5hbWUgPSAiTWF4IEdyb3d0aCBSYXRlIE1hbGVzIikKCmZpZyA8LSBmaWcgJT4lIGFkZF9saW5lcyh4ID0gdGltZSwgeSA9IHByZWRjMiwgbmFtZSA9ICJXZWlnaHQgRmVtYWxlcyIpCmZpZyA8LSBmaWcgJT4lIGFkZF9saW5lcyh4ID0gdGltZSwgeSA9IGdyYzIsIG5hbWUgPSAiR3Jvd3RoIFJhdGUgRmVtYWxlcyIsIHlheGlzID0gInkyIikKZmlnIDwtIGZpZyAlPiUgYWRkX21hcmtlcnMoeCA9IG1heGVmeDIsIHkgPSBtYXhlZnkyLCBuYW1lID0gIk1heCBHcm93dGggUmF0ZSBGZW1hbGVzIiwgeWF4aXMgPSAieTIiKQpmaWcgPC0gZmlnICU+JSBhZGRfc2VnbWVudHMoY29sb3IgPSBJKCJncmF5IikseCA9IG1heGVmeDIseGVuZD1tYXhlZngyLCB5ID0gMCx5ZW5kID1tYXhlZnkyLCB5YXhpcyA9ICJ5MiIsbmFtZSA9ICJNYXggR3Jvd3RoIFJhdGUgRmVtYWxlcyIpCgpmaWcgPC0gZmlnICU+JSBsYXlvdXQoCiAgdGl0bGUgPSAiR3Jvd3RoIEN1cnZlIHdpdGggR3Jvd3RoIHJhdGUiLCB5YXhpczIgPSBheSwKICB4YXhpcyA9IGxpc3QodGl0bGU9IkFnZShkYXlzKSIpLAogIHlheGlzID0gbGlzdCh0aXRsZT0iV2VpZ2h0KGcpIikKKQpmaWcKYGBgCgoKCiMjIyMjIEltcG9ydGluZyBNaWxraW5nIERhdGFzZXQKCiMjIEltcG9ydGluZyBkYXRhc2V0IGZyb20gdHh0CgpgYGB7cn0Kc2V0d2QoIn4vSW5zeW5jL2xlb25hcmRvZ2xvcmlhQHVlbmYuYnIvR29vZ2xlIERyaXZlL01FTlRPUklBX1IvTW9uZGF5X21lZXRpbmciKQpkYXRhX21pbGtnIDwtIHJlYWQudGFibGUoImRhdGFfbWlsay50eHQiLCBoID0gVCkgJT4lIAogIG11dGF0ZShhbmltYWw9ZmFjdG9yKGFuaW1hbCksQ0c9ZmFjdG9yKENHKSkgJT4lIAogIGdyb3VwZWREYXRhKE1ZfkRJTXxhbmltYWwsZGF0YT0uKSAjQ3JlYXRpbmcgZGF0YXNldCBpbmRpY2F0aW5nIGFuaW1hbCByZXBlYXRlZCBtZWFzdXJlcyBieSBkYXlzIGluIG1pbGsKYGBgCgojIyMjIyBXT09EIG1vZGVsLCB3aXRob3V0IGZpeGVkIGVmZmVjdApgYGB7cn0KdGV0YT1jKCk7ICAgQUljYz1jKCk7Cgptb2RlbDAwPW5sbWUoTVl+YSooRElNKipiKSpleHAoLWsqRElNKSxmaXhlZD1saXN0KGErYitrfjEpLHJhbmRvbT1wZERpYWcoYStifjEpLAogICAgICAgICAgICAgY29udHJvbD1ubG1lQ29udHJvbChtYXhJdGVyPTEwMCksCiAgICAgICAgICAgICBkYXRhPWRhdGFfbWlsa2csCiAgICAgICAgICAgICBzdGFydD1saXN0KGZpeGVkID1jKDIwLDAuMSwwLjAwMykpLG5hLmFjdGlvbj1uYS5vbWl0KSAgICAgICAKCih0ZXRhWzFdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDApKSRkZikKKEFJY2NbMV09LTIqbW9kZWwwMCRsb2dMaWsrMip0ZXRhWzFdKygyKnRldGFbMV0qKHRldGFbMV0rMSkpLyhucm93KGRhdGFfbWlsa2cpLXRldGFbMV0tMSkpCmBgYAoKIyMjIyMgIFdPT0QgbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGluIG90aGVyIHdvcmRzLCB3ZSB3aWxsIGZpdCBhIHBhcmFtZXRlciBmb3IgZWFjaCBzZXgKYGBge3J9CnN0MDE9YyhyZXAoc3VtbWFyeShtb2RlbDAwKSQgY29lZmZpY2llbnRzJGZpeGVkW1sxXV0sbGVuZ3RoKGxldmVscyhkYXRhX21pbGtnJENHKSkpLHJlcChzdW1tYXJ5KG1vZGVsMDApJCBjb2VmZmljaWVudHMkZml4ZWRbWzJdXSxsZW5ndGgobGV2ZWxzKGRhdGFfbWlsa2ckQ0cpKSkscmVwKHN1bW1hcnkobW9kZWwwMCkkIGNvZWZmaWNpZW50cyRmaXhlZFtbM11dLGxlbmd0aChsZXZlbHMoZGF0YV9taWxrZyRDRykpKSkgI3N0YXJ0aW5nIHZhbHVlcyBiYXNlZCBvbiB0aGUgbW9kZWwwMCBvdXRwdXQKCm1vZGVsMDE9bmxtZShNWX5hKihESU0qKmIpKmV4cCgtaypESU0pLGZpeGVkPWxpc3QoYStiK2t+Q0ctMSkscmFuZG9tPXBkRGlhZyhhfjEpLAogICAgICAgICAgICAgY29udHJvbD1ubG1lQ29udHJvbChtYXhJdGVyPTEwMCksCiAgICAgICAgICAgICBkYXRhPWRhdGFfbWlsa2csc3RhcnQ9c3QwMSxuYS5hY3Rpb249bmEub21pdCkKCgoodGV0YVsyXT1hdHRyaWJ1dGVzKGxvZ0xpayhtb2RlbDAxKSkkZGYpCihBSWNjWzJdPS0yKm1vZGVsMDEkbG9nTGlrKzIqdGV0YVsyXSsoMip0ZXRhWzJdKih0ZXRhWzJdKzEpKS8obnJvdyhkYXRhX21pbGtnKS10ZXRhWzJdLTEpKQpgYGAKCiMjIyMjICBXT09EIG1vZGVsIHdpdGggY2xhc3Mgb2YgZml4ZWQgZWZmZWN0LCBpbiBvdGhlciB3b3Jkcywgd2Ugd2lsbCBmaXQgYSBwYXJhbWV0ZXIgZm9yIGVhY2ggc2V4LCBwbHVzICh3ZWlnaHRzID0gaGV0ZXJvZ2VuZW91cyB2YXJpYW5jZSBhbG9uZyB0aGUgdGltZSwgdmFyUG93ZXI9IHBvd2VyIHZhcmlhbmNlIGZ1bmN0aW9uIGlzIGRlZmluZWQgYXMgczIodikgPSB8dnxeKDIqdCkpCgpgYGB7cn0KcGFyZj1jKCkKZm9yIChpIGluIDE6bGVuZ3RoKHN1bW1hcnkobW9kZWwwMSkkIGNvZWZmaWNpZW50cyRmaXhlZCkpICAgICAgewpwYXIxPXN1bW1hcnkobW9kZWwwMSkkIGNvZWZmaWNpZW50cyRmaXhlZFtbaV1dCnBhcmY9cmJpbmQocGFyZixwYXIxKQogfSAjc3RhcnRpbmcgdmFsdWVzIGJhc2VkIG9uIHRoZSBtb2RlbDAxIG91dHB1dCwgZWFjaCBjbGFzcyBoYXMgaXRzIG93biBzdGFydCB2YWx1ZQoKbW9kZWwwMj1ubG1lKE1ZfmEqKERJTSoqYikqZXhwKC1rKkRJTSksZml4ZWQ9bGlzdChhK2Ira35DRy0xKSxyYW5kb209cGREaWFnKGF+MSksCiAgICAgICAgICAgICBjb250cm9sPW5sbWVDb250cm9sKG1heEl0ZXI9NTAwKSwKICAgICAgICAgICAgIGRhdGE9ZGF0YV9taWxrZyxzdGFydD1wYXJmLG5hLmFjdGlvbj1uYS5vbWl0LHdlaWdodHM9IHZhclBvd2VyKCkpICAgICAgIAoKaW50ZXJ2YWxzKG1vZGVsMDIpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCih0ZXRhWzNdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDIpKSRkZikKKEFJY2NbM109LTIqbW9kZWwwMiRsb2dMaWsrMip0ZXRhWzNdKygyKnRldGFbM10qKHRldGFbM10rMSkpLyhucm93KGRhdGFfbWlsa2cpLXRldGFbM10tMSkpCmBgYAoKIyMjIyMgIFdPT0QgbW9kZWwgd2l0aCBjbGFzcyBvZiBmaXhlZCBlZmZlY3QsIGluIG90aGVyIHdvcmRzLCB3ZSB3aWxsIGZpdCBhIHBhcmFtZXRlciBmb3IgZWFjaCBzZXgsIHBsdXMgKGNvcnIgPSBtb2RlbGluZyBhbmltYWwgcmVwZWF0ZWQgbWVhc3VyZXMsIENBUjEgPSBjb250aW5vdXMgYXV0b3JlZ3Jlc3NpdmUpCmBgYHtyfQpwYXJmPWMoKQpmb3IgKGkgaW4gMTpsZW5ndGgoc3VtbWFyeShtb2RlbDAxKSQgY29lZmZpY2llbnRzJGZpeGVkKSkgICAgICB7CnBhcjE9c3VtbWFyeShtb2RlbDAxKSQgY29lZmZpY2llbnRzJGZpeGVkW1tpXV0KcGFyZj1yYmluZChwYXJmLHBhcjEpCiB9ICNzdGFydGluZyB2YWx1ZXMgYmFzZWQgb24gdGhlIG1vZGVsMDEgb3V0cHV0LCBlYWNoIGNsYXNzIGhhcyBpdHMgb3duIHN0YXJ0IHZhbHVlCgptb2RlbDAzPW5sbWUoTVl+YSooRElNKipiKSpleHAoLWsqRElNKSxmaXhlZD1saXN0KGErYitrfkNHLTEpLHJhbmRvbT1wZERpYWcoYX4xKSwKICAgICAgICAgICAgIGNvbnRyb2w9bmxtZUNvbnRyb2wobWF4SXRlcj0xMDApLAogICAgICAgICAgICAgZGF0YT1kYXRhX21pbGtnLHN0YXJ0PXBhcmYsbmEuYWN0aW9uPW5hLm9taXQsY29ycj1jb3JDQVIxKCkpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCih0ZXRhWzRdPWF0dHJpYnV0ZXMobG9nTGlrKG1vZGVsMDMpKSRkZikKKEFJY2NbNF09LTIqbW9kZWwwMyRsb2dMaWsrMip0ZXRhWzRdKygyKnRldGFbNF0qKHRldGFbNF0rMSkpLyhucm93KGRhdGFfbWlsa2cpLXRldGFbNF0tMSkpCmBgYAoKIyMjIyMgRnVsbCBXT09EIG1vZGVsIHdpdGggY2xhc3Mgb2YgZml4ZWQgZWZmZWN0LCBoZXRlcm9nZW5lb3VzIHZhcmlhbmNlIGFsb25nIHRoZSB0aW1lLCByZXBlYXRlZCBtZWFzdXJlcwpgYGB7cn0KcGFyZj1jKCkKZm9yIChpIGluIDE6bGVuZ3RoKHN1bW1hcnkobW9kZWwwMykkIGNvZWZmaWNpZW50cyRmaXhlZCkpICAgICAgewpwYXIxPXN1bW1hcnkobW9kZWwwMykkIGNvZWZmaWNpZW50cyRmaXhlZFtbaV1dCnBhcmY9cmJpbmQocGFyZixwYXIxKQogfQoKbW9kZWwwND1ubG1lKE1ZfmEqKERJTSoqYikqZXhwKC1rKkRJTSksZml4ZWQ9bGlzdChhK2Ira35DRy0xKSxyYW5kb209cGREaWFnKGErYitrfjEpLAogICAgICAgICAgICAgY29udHJvbD1ubG1lQ29udHJvbChtYXhJdGVyPTEwMCksCiAgICAgICAgICAgICBkYXRhPWRhdGFfbWlsa2csc3RhcnQ9cGFyZixuYS5hY3Rpb249bmEub21pdCxjb3JyPWNvckNBUjEoKSx3ZWlnaHRzPSB2YXJQb3dlcigpKSAKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKKHRldGFbNV09YXR0cmlidXRlcyhsb2dMaWsobW9kZWwwNCkpJGRmKQooQUljY1s1XT0tMiptb2RlbDA0JGxvZ0xpaysyKnRldGFbNV0rKDIqdGV0YVs1XSoodGV0YVs1XSsxKSkvKG5yb3coZGF0YV9taWxrZyktdGV0YVs1XS0xKSkKYGBgCgojIyMjIyBtdWx0aS1tb2RlbCBzZWxlY3Rpb24gZnJhbWV3b3JrIChCdXJuaGFtIGFuZCBBbmRlcnNvbiwgMjAwNCkKYGBge3J9CgpkZWx0YT1jKCkKZm9yKGkgaW4gMTogbGVuZ3RoKEFJY2MpKXsKZGVsdGFbaV09QUljY1tpXS1taW4oQUljYykKfQoKd3Bybz1jKCkKZm9yKGkgaW4gMTpsZW5ndGgoQUljYykpewp3cHJvW2ldPWV4cCgtZGVsdGFbaV0vMikKfQpzdW0od3Byb1sxOmxlbmd0aChBSWNjKV0pCgp3cHJvYj1jKCkKZm9yKGkgaW4gMTogbGVuZ3RoKEFJY2MpKXsKd3Byb2JbaV09ZXhwKC1kZWx0YVtpXS8yKS9zdW0od3Byb1sxOmxlbmd0aChBSWNjKV0pCn0KCgpFUj1jKCkKZm9yKGkgaW4gMTogbGVuZ3RoKEFJY2MpKXsKRVJbaV09bWF4KHdwcm9iKS93cHJvYltpXQp9CgoocXVhZHJvLmFrYWlrZT1kYXRhLmZyYW1lKHRldGEsQUlDYz1BSWNjLGRlbHRhLHdwcm9iLEVSKSkKYGBgCgoKIyMjIyMgRXh0cmFjdGluZyB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgb2YgdGhlIHBhcmFtZXRlcnMgYWZ0ZXIgY2hvb3NpbmcgdGhlIGJlc3QgbW9kZWwKYGBge3J9CmludGVydmFscyhtb2RlbDA0KQpgYGAKCiMjIyMjIFByZWRpY3Rpb24gTVkgYW5kIFRNWQpgYGB7cn0KTVdvb2RoYXQ9ZnVuY3Rpb24oYSxiLGssYWdlKXsKICB5PShhKmFnZV5iKSpleHAoLWsqYWdlKQogIHJldHVybih5KQp9CgojbWlsayB5ZWxkIHdpdGggV29vZCwgTVlsID0gZmlyc3QgZGF5LE1ZdT1sYXN0IGRheQpNWVdvb2Q9ZnVuY3Rpb24oYSxiLGssTVlsLE1ZdSl7CiAgaW50PWMoKQogIGZvcihpIGluIDE6bGVuZ3RoKGEpKXsKICAgIGludGVncmFuZCA8LSBmdW5jdGlvbihhZ2UpIHthW2ldKihhZ2UqKmJbaV0pKmV4cCgta1tpXSphZ2UpfQogICAgaW50MSA8LSB0cnkoaW50ZWdyYXRlKGludGVncmFuZCwgbG93ZXI9TVlsLCB1cHBlcj1NWXUpLCBzaWxlbnQgPSBUUlVFKQogICAgaWYoaW5oZXJpdHMoaW50ICwndHJ5LWVycm9yJykpewogICAgICB3YXJuaW5nKGFzLnZlY3RvcihpbnQpKQogICAgICBpbnRlZ3JhdGVkIDwtIE5BX3JlYWxfCiAgICB9IGVsc2UgewogICAgICBpbnRlZ3JhdGVkIDwtIGludDFbMV0KICAgIH0KICAgIGludD1yYmluZChpbnQsaW50MVsxXSkKICB9CgogIHJldHVybihpbnQpCn0KYGBgCiMjIyMjIFRvdGFsIE1pbGsgWWllbGQKCmBgYHtyfQooVE1ZQ0cyMj1NWVdvb2QoYT1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsyLDJdKSwKICAgICAgICAgICAgICAgICBiPWFzLm51bWVyaWMoaW50ZXJ2YWxzKG1vZGVsMDQpJGZpeGVkWzgsMl0pLAogICAgICAgICAgICAgICAgIGs9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbMTQsMl0pLAogICAgICAgICAgICAgICAgIE1ZbD0xMCxNWXU9MzA1KSkKCgooVE1ZQ0czMj1NWVdvb2QoYT1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFs1LDJdKSwKICAgICAgICAgICAgICAgYj1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsxMSwyXSksCiAgICAgICAgICAgICAgIGs9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbMTcsMl0pLAogICAgICAgICAgICAgICBNWWw9MTAsTVl1PTMwNSkpCgpUTVlDRzIyW1sxXV0tVE1ZQ0czMltbMV1dCmBgYAoKIyMjIyMgY3JlYXRpbmcgaW50ZXJhY3RpdmUgZ3JhcGhpY3Mgd2l0aCBwbG90bHkKYGBge3J9CgphZ2U9c2VxKDEsMzA1LGJ5PTAuMjUpCiNwcmVkaWN0aW9ucwpwcmVkYzE9TVdvb2RoYXQoYT1hcy5udW1lcmljKGludGVydmFscyhtb2RlbDA0KSRmaXhlZFsyLDJdKSxiPWFzLm51bWVyaWMoaW50ZXJ2YWxzKG1vZGVsMDQpJGZpeGVkWzgsMl0pLGs9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbMTQsMl0pLGFnZT1hZ2UpCnByZWRjMj1NV29vZGhhdChhPWFzLm51bWVyaWMoaW50ZXJ2YWxzKG1vZGVsMDQpJGZpeGVkWzUsMl0pLGI9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbMTEsMl0pLGs9YXMubnVtZXJpYyhpbnRlcnZhbHMobW9kZWwwNCkkZml4ZWRbMTcsMl0pLGFnZT1hZ2UpCgojIyMgTXVsdGlwbGUgWSBBeGVzCgojYXkgPC0gbGlzdCgKIyAgdGlja2ZvbnQgPSBsaXN0KGNvbG9yID0gImJsYWNrIiksCiMgIG92ZXJsYXlpbmcgPSAieSIsCiMgIHNpZGUgPSAicmlnaHQiLAojICB0aXRsZSA9ICJEYWlseSBXZWlnaHQgR2FpbihnKSIKIykKZmlnIDwtIHBsb3RfbHkoKQpmaWcgPC0gZmlnICU+JSBhZGRfbGluZXMoeCA9IGFnZSwgeSA9IHByZWRjMSwgbmFtZSA9ICJNaWxrIFlpZWxkIENHMjIiKQoKZmlnIDwtIGZpZyAlPiUgYWRkX2xpbmVzKHggPSBhZ2UsIHkgPSBwcmVkYzIsIG5hbWUgPSAiTWlsayBZaWVsZCBDRzMyIikKCgpmaWcgPC0gZmlnICU+JSBsYXlvdXQoCiAgdGl0bGUgPSAiV09PRCBNaWxrIGN1cnZlIiwKICB4YXhpcyA9IGxpc3QodGl0bGU9IkRJTShkYXlzKSIpLAogIHlheGlzID0gbGlzdCh0aXRsZT0iTVkgKGtnKSIpCikKZmlnCmBgYAo=